package com.punai.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.multi.ListValueMap;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.punai.common.core.domain.CommonConstants;
import com.punai.common.core.domain.TreeBo;
import com.punai.common.core.domain.entity.SysPnDictionary;
import com.punai.common.core.redis.PnDictCache;
import com.punai.common.exception.ServiceException;
import com.punai.common.utils.PnDictUtil;
import com.punai.common.utils.SecurityUtils;
import com.punai.config.sqlitedao.SqliteDictionaryRepository;
import com.punai.system.mapper.SysPnDictionaryMapper;
import com.punai.system.service.SysPnDictionaryService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class SysPnDictionaryServiceImpl extends ServiceImpl<SysPnDictionaryMapper, SysPnDictionary> implements SysPnDictionaryService {

    Logger logger = LoggerFactory.getLogger(SysPnDictionaryServiceImpl.class);

    @Autowired
    private SysPnDictionaryMapper sysPnDictionaryMapper;
    @Autowired
    private SqliteDictionaryRepository sqliteDictionaryRepository;
    @Autowired
    private PnDictCache pnDictCache;


    @PostConstruct
    public void init() {
        reloadCache();
    }

    @Override
    public List<TreeBo> treelist(Long companyId) {
        List<SysPnDictionary> list = this.list(
                Wrappers.<SysPnDictionary>lambdaQuery()
                        .eq(SysPnDictionary::getCompanyId, companyId)
        );
        List<TreeBo> convert = TreeBo.convert(list);
        return convert;
    }

    @Override
    public String getNextCode(String pid, Long companyId) {
        String pcode = "";
        if (StrUtil.isNotEmpty(pid) && !StrUtil.equals(pid, CommonConstants.DEF_PID)) {
            SysPnDictionary sysPnDictionary = sysPnDictionaryMapper.selectById(pid);
            pcode = sysPnDictionary.getCode();
        }
        return sysPnDictionaryMapper.getNextCode(pid,pcode,companyId);
    }

    @Override
    public int insertPnDict(SysPnDictionary vo) {
        // 判定code
        String code = vo.getCode();
        String parentId = vo.getParentId();
        if (!NumberUtil.isLong(code)) {
            throw new ServiceException("编码不符合规则!");
        }
        if (StrUtil.equals(CommonConstants.DEF_PID,parentId)) {
            int length = StrUtil.length(code);
            if (length > 3) {
                throw new ServiceException("编码不符合规则!");
            }
            if (length < 3) {
                code = StrUtil.fillBefore(code,'0',3);
                vo.setCode(code);
            }
        }
        if (isExistCode(vo.getCode(), vo.getCompanyId())) {
            throw new ServiceException("编码重复!");
        }
        Integer maxSorting = getMaxSorting(vo.getParentId());
        vo.setSorting(maxSorting + 1);
        int insert = sysPnDictionaryMapper.insert(vo);
        if (insert > 0) {
            pnDictCache.putInCache(vo);
            sqliteDictionaryRepository.save(vo);
        }
        return insert;
    }

    @Override
    public int updatePnDict(SysPnDictionary vo) {
        int update = sysPnDictionaryMapper.update(vo,
                Wrappers.<SysPnDictionary>lambdaUpdate()
                        .set(SysPnDictionary::getName, vo.getName())
                        .set(SysPnDictionary::getValue, vo.getValue())
                        .set(SysPnDictionary::getVarParams, vo.getVarParams())
                        .set(SysPnDictionary::getRemarks, vo.getRemarks())
                        .eq(SysPnDictionary::getId, vo.getId())
        );
        if (update > 0) {
            pnDictCache.putInCache(vo);
            sqliteDictionaryRepository.save(vo);
        }
        return update;
    }

    @Override
    public int delete(String id,Long companyId) {
        if (isExistChildren(id,companyId)) {
            throw new ServiceException("存在子目录,请先删除子目录!");
        }
        SysPnDictionary sysPnDictionary = sysPnDictionaryMapper.selectById(id);
        int result = sysPnDictionaryMapper.deleteById(id);
        if (result > 0) {
            pnDictCache.removeChildFromCache(sysPnDictionary.getCode());
            sqliteDictionaryRepository.delete(sysPnDictionary);
        }
        return result;
    }

    @Override
    public Collection<SysPnDictionary> getChildren(String code, Long companyId) {
        Collection<SysPnDictionary> dictionarys = PnDictUtil.getDictionarys(code);
        if (CollUtil.isEmpty(dictionarys)) {
            SysPnDictionary byCode = getByCode(code);
            if (byCode != null) {
                String id = byCode.getId();
                dictionarys = getChildren(id);
                if (CollUtil.isNotEmpty(dictionarys)) {
                    dictionarys = dictionarys.stream().sorted(Comparator.comparing(SysPnDictionary::getSorting)).collect(Collectors.toList());
                    for (SysPnDictionary dictionary : dictionarys) {
                        pnDictCache.putInCache(dictionary);
                    }
                }

            }
        }
        return dictionarys;
    }

    @Override
    public Collection<SysPnDictionary> getAllChildren(String code, Long companyId) {
        Collection<SysPnDictionary> all = PnDictUtil.getDictionarysWithChild(code);
        Collection<SysPnDictionary> roots = new ArrayList<>();
        if (CollUtil.isEmpty(all)) {
            SysPnDictionary byCode = getByCode(code);
            if (byCode != null) {
                roots.add(byCode);
                getChildrenAndSet(byCode);
            }
        }
        return roots;
    }

    @Override
    public int reloadCache() {
        List<SysPnDictionary> sysPnDictionaries = sysPnDictionaryMapper.selectList(null);
        if (CollUtil.isNotEmpty(sysPnDictionaries)) {
            ListValueMap<String, SysPnDictionary> collect = sysPnDictionaries.stream()
                    .collect(Collectors.groupingBy(SysPnDictionary::getParentId, ListValueMap::new, Collectors.toList()));
            List<SysPnDictionary> roots = collect.get(CommonConstants.DEF_PID);
            for (SysPnDictionary root : roots) {
                pnDictCache.putInCache(root);
                setChildToCache(root.getId(), collect);
            }
        }
        return 1;
    }

    @Override
    public int resetSqlite() {
        Long companyId = SecurityUtils.getCompanyId();
        List<SysPnDictionary> sysPnDictionaries = sysPnDictionaryMapper.selectList(
                Wrappers.<SysPnDictionary>lambdaQuery()
                        .eq(SysPnDictionary::getCompanyId, companyId)
        );
        try {
            sqliteDictionaryRepository.beginTransaction();
        } catch (SQLException e) {
            e.printStackTrace();
            throw new ServiceException("重置sqlite 失败!");
        }
        try{
            sqliteDictionaryRepository.deleteAll();
            if (CollUtil.isNotEmpty(sysPnDictionaries)) {
                for (SysPnDictionary root : sysPnDictionaries) {
                    sqliteDictionaryRepository.save(root);
                }
            }
            sqliteDictionaryRepository.commitTransaction();
            logger.info("重置sqlite 成功 commitTransaction 成功!");
        }catch (Exception e) {
            try {
                sqliteDictionaryRepository.rollbackTransaction();
                logger.error("重置sqlite 失败 rollbackTransaction 成功!",e);
            } catch (SQLException ex) {
                ex.printStackTrace();
                logger.error("重置sqlite 失败 rollbackTransaction 失败!",ex);
            }
            return 0;
        }
        return 1;
    }


    private void setChildToCache(String pid,  ListValueMap<String, SysPnDictionary> allLevelMap) {
        List<SysPnDictionary> children = allLevelMap.get(pid);
        if (CollUtil.isNotEmpty(children)) {
            for (SysPnDictionary child : children) {
                pnDictCache.putInCache(child);
                setChildToCache(child.getId(), allLevelMap);
            }
        }
    }


    private void getChildrenAndSet(SysPnDictionary byCode) {
        String id = byCode.getId();
        List<SysPnDictionary> roots = getChildren(id);
        if (CollUtil.isNotEmpty(roots)) {
            roots = roots.stream().sorted(Comparator.comparing(SysPnDictionary::getSorting)).collect(Collectors.toList());
            byCode.setChildren(roots);
            for (SysPnDictionary dictionary : roots) {
                pnDictCache.putInCache(dictionary);
               getChildrenAndSet(dictionary);
            }
        }
    }


    private Boolean isExistCode(String code,Long companyId) {
        Long aLong = sysPnDictionaryMapper.selectCount(
                Wrappers.<SysPnDictionary>lambdaQuery()
                        .eq(SysPnDictionary::getCode, code)
                        .eq(SysPnDictionary::getCompanyId, companyId)
        );
        return aLong > 0;
    }

    private Boolean isExistChildren(String pid,Long companyId) {
        Long aLong = sysPnDictionaryMapper.selectCount(
                Wrappers.<SysPnDictionary>lambdaQuery()
                        .eq(SysPnDictionary::getParentId, pid)
                        .eq(SysPnDictionary::getCompanyId, companyId)
        );
        return aLong > 0;
    }

    private Integer getMaxSorting(String pid) {
        Long companyId = SecurityUtils.getCompanyId();
        return sysPnDictionaryMapper.getMaxSorting(pid,companyId);
    }

    private SysPnDictionary getByCode(String code) {
        return sysPnDictionaryMapper.selectOne(
                Wrappers.<SysPnDictionary>lambdaQuery()
                        .eq(SysPnDictionary::getCode, code)
        );
    }

    private List<SysPnDictionary> getChildren(String pid) {
        return sysPnDictionaryMapper.selectList(
                Wrappers.<SysPnDictionary>lambdaQuery()
                        .eq(SysPnDictionary::getParentId, pid)
        );
    }



}
